/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xos_http.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../data_struct/head.h"
#include "../helper_proc/head.h"
#include "../helper_objs/head.h"
#include "../config/head.h"
#include "../macro/head.h"
#include "../impl/head.h"
#include "../msg/head.h"
#include "msg_notify.h"
#include "notify_tcp.h"
#include "notify_udp.h"

namespace xos_http
{

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    notify_tcp::notify_tcp()
    {
    }

    notify_tcp::~notify_tcp()
    {
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // bool[0]      : if success
    // 
    // msg[0]       : msg type
    // 
    // str[1]       : local ip
    // int[1]       : local port
    // 
    // str[2]       : peer ip
    // int[2]       : peer port
    // 
    // void[0]      : net::i_connection*
    // void[1]      : user data
    // 
    int notify_tcp::post_init_server( xos_box::i_msg *& pMsg, connection * pConnect )
    {
        int ret = 0;

        if( !pMsg )
        {
            pMsg = mgr::container()->box()->msg();
        }

        if( 0 == ret )
        {
            pMsg->set_str( 1, pConnect->get_local_ip( 0, 0, 0 ) );
            pMsg->set_int( 1, pConnect->get_local_port( 0 ) );
            pMsg->set_msg( 0, net::NET_TCP_INIT_SERVER );
            pMsg->set_void( 1, pConnect->xos_connect() );
        }

        if( 0 == ret )
        {
            helper_net_callback::get()->notify( pMsg );
        }

        xos_stl::release_interface( pMsg );

        return ret;
    }

    // bool[0]      : if success
    // 
    // msg[0]       : msg type
    // 
    // str[1]       : local ip
    // int[1]       : local port
    // 
    // str[2]       : peer ip
    // int[2]       : peer port
    // 
    // void[0]      : net::i_connection*
    // void[1]      : user data
    // 
    int notify_tcp::post_connect( xos_box::i_msg *& pMsg, connection * pConnect )
    {
        int ret = 0;

        if( !pMsg )
        {
            pMsg = mgr::container()->box()->msg();
        }

        if( 0 == ret )
        {
            pMsg->set_str( 2, pConnect->get_peer_ip( 0, 0, 0 ) );
            pMsg->set_int( 2, pConnect->get_peer_port( 0 ) );
            pMsg->set_msg( 0, net::NET_TCP_CONNECT );
            pMsg->set_void( 1, pConnect->xos_connect() );
        }

        if( 0 == ret )
        {
            helper_net_callback::get()->notify( pMsg );
        }

        xos_stl::release_interface( pMsg );

        return ret;
    }

    // bool[0]      : if success
    // 
    // bool[1]      : close after send
    // bool[2]      : quit after send
    // 
    // msg[0]       : msg type
    // 
    // int[0]       : bytes
    // buf[0]       : buf
    // 
    // void[0]      : net::i_connection*
    // 
    int notify_tcp::post_send( xos_box::i_msg *& pMsg, connection * pConnect, const char * lpszData, bool bCloseAfterSend, bool bQuitAfterSend )
    {
        int ret = 0;

        int nLen = mgr::container()->crt()->strlen( lpszData );

        if( 0 == ret )
        {
            ret = post_send( pMsg, pConnect, lpszData, nLen, bCloseAfterSend, bQuitAfterSend );
        }

        return ret;
    }

    // bool[0]      : if success
    // 
    // bool[1]      : close after send
    // bool[2]      : quit after send
    // 
    // msg[0]       : msg type
    // 
    // int[0]       : bytes
    // buf[0]       : buf
    // 
    // void[0]      : net::i_connection*
    // 
    int notify_tcp::post_send( xos_box::i_msg *& pMsg, connection * pConnect, const char * lpszData, int nLen, bool bCloseAfterSend, bool bQuitAfterSend )
    {
        int ret = 0;

        xos_box::i_buf * pBuf = mgr::container()->box()->buf();

        if( 0 == ret )
        {
            pBuf->add_data( lpszData, nLen, true );
            ret = post_send( pMsg, pConnect, pBuf, bCloseAfterSend, bQuitAfterSend );
            pBuf = 0;
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    // bool[0]      : if success
    // 
    // bool[1]      : close after send
    // bool[2]      : quit after send
    // 
    // msg[0]       : msg type
    // 
    // int[0]       : bytes
    // buf[0]       : buf
    // 
    // void[0]      : net::i_connection*
    // 
    int notify_tcp::post_send( xos_box::i_msg *& pMsg, connection * pConnect, xos_box::i_buf *& pBuf, bool bCloseAfterSend, bool bQuitAfterSend )
    {
        int ret = 0;

        net::i_connect * pNetConnect = pConnect->get_net_connect();
        if( !pMsg )
        {
            pMsg = mgr::container()->box()->msg();
        }

        if( 0 == ret )
        {
            pMsg->set_bool( 1, bCloseAfterSend );
            pMsg->set_bool( 2, bQuitAfterSend );
            pMsg->set_msg( 0, net::NET_TCP_SEND );
            pMsg->set_buf( 0, pBuf );
            pBuf = 0;
        }

        if( 0 == ret )
        {
            pMsg->set_void( 0, pNetConnect );
        }

        if( 0 == ret )
        {
            pNetConnect->add_ref();
            helper_net_callback::get()->notify( pMsg );
        }

        xos_stl::release_interface( pMsg );

        return ret;
    }

    int notify_tcp::post_send( connection * pConnect, xos_box::i_list * pList, bool bCloseAfterSend, bool bQuitAfterSend )
    {
        int ret = 0;

        xos_box::i_buf * pBuf = 0;
        while( ( pBuf = (xos_box::i_buf *)pList->front( 0 ) ) )
        {
            xos_box::i_msg * pMsg = 0;
            pList->pop_front();
            if( !pList->front( 0 ) )
            {
                post_send( pMsg, pConnect, pBuf, bCloseAfterSend, bQuitAfterSend );
            }
            else
            {
                post_send( pMsg, pConnect, pBuf, false, false );
            }
            xos_stl::release_interface( pBuf );
        }

        return ret;
    }

    // bool[0]      : if success
    // 
    // msg[0]       : msg type
    // 
    // void[0]      : net::i_connection*
    // 
    int notify_tcp::post_shutdown( xos_box::i_msg *& pMsg, connection * pConnect, int how )
    {
        int ret = 0;

        net::i_connect * pNetConnect = pConnect->get_net_connect();
        if( !pMsg )
        {
            pMsg = mgr::container()->box()->msg();
        }

        if( 0 == ret )
        {
            pMsg->set_msg( 0, net::NET_TCP_SHUTDOWN );
            pMsg->set_void( 0, pNetConnect );
            pMsg->set_int( 0, how );
            pNetConnect->add_ref();
            helper_net_callback::get()->notify( pMsg );
        }

        xos_stl::release_interface( pMsg );

        return ret;
    }

    // bool[0]      : if success
    // 
    // msg[0]       : msg type
    // 
    // m_nData[1]   : linger.l_onoff
    // m_nData[2]   : linger.l_linger
    // 
    // close : 1,1
    // reset : 0,0
    // 
    // void[0]      : net::i_connection*
    // 
    int notify_tcp::helper_post_close( xos_box::i_msg *& pMsg, connection * pConnect, int onoff, int linger )
    {
        int ret = 0;

        net::i_connect * pNetConnect = pConnect->get_net_connect();
        if( !pMsg )
        {
            pMsg = mgr::container()->box()->msg();
        }

        if( 0 == ret )
        {
            pMsg->set_msg( 0, net::NET_TCP_CLOSE );
            pMsg->set_void( 0, pNetConnect );
            pMsg->set_int( 1, onoff );
            pMsg->set_int( 2, linger );
            pNetConnect->add_ref();
            helper_net_callback::get()->notify( pMsg );
        }

        xos_stl::release_interface( pMsg );

        return ret;
    }

    // bool[0]      : if success
    // 
    // msg[0]       : msg type
    // 
    // m_nData[1]   : linger.l_onoff
    // m_nData[2]   : linger.l_linger
    // 
    // close : 1,1
    // reset : 0,0
    // 
    // void[0]      : net::i_connection*
    // 
    int notify_tcp::post_close( xos_box::i_msg *& pMsg, connection * pConnect )
    {
        int ret = 0;
        ret = helper_post_close( pMsg, pConnect, 1, 1 );
        return ret;
    }

    // bool[0]      : if success
    // 
    // msg[0]       : msg type
    // 
    // m_nData[1]   : linger.l_onoff
    // m_nData[2]   : linger.l_linger
    // 
    // close : 1,1
    // reset : 0,0
    // 
    // void[0]      : net::i_connection*
    // 
    int notify_tcp::post_reset( xos_box::i_msg *& pMsg, connection * pConnect )
    {
        int ret = 0;
        ret = helper_post_close( pMsg, pConnect, 0, 0 );
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    // 
    // proc
    // 
    int notify_tcp::proc( xos_box::i_msg *& pMsg )
    {
        int ret = 1;

        int nMsg = pMsg->get_msg();

        switch( nMsg )
        {
            case net::NET_TCP_INIT_SERVER:
            {
                on_init_server( pMsg );
            }
            break;
            case net::NET_TCP_ACCEPT:
            {
                on_accept( pMsg );
            }
            break;
            case net::NET_TCP_CONNECT:
            {
                on_connect( pMsg );
            }
            break;
            case net::NET_TCP_SEND:
            {
                on_send( pMsg );
            }
            break;
            case net::NET_TCP_RECV:
            {
                on_recv( pMsg );
            }
            break;
            case net::NET_TCP_SHUTDOWN:
            {
                on_shutdown( pMsg );
            }
            break;
            case net::NET_TCP_CLOSE:
            {
                on_close( pMsg );
            }
            break;

            default:
            {
                ret = 0;
            }
            break;
        }

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // bool[0]      : if success
    // 
    // msg[0]       : msg type
    // 
    // str[1]       : local ip
    // int[1]       : local port
    // 
    // str[2]       : peer ip
    // int[2]       : peer port
    // 
    // void[0]      : net::i_connection*
    // void[1]      : user data
    // 
    int notify_tcp::on_init_server( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        net::i_connect * pNetConnect = (net::i_connect *)pMsg->get_void( 0, 0 );
        connection * pConnect = (connection *)pNetConnect->get_user_data();

        bool bRet = pMsg->get_bool( 0, 0 );

        // 稍后xos_net会回调on_release来释放这里的connection
        // 所以这里不要释放这个connection
        if( ( 0 == ret ) && !bRet )
        {
            pNetConnect->release_ref();
            ret = 1;
        }

        if( 0 == ret )
        {
            const char * lpszLocalIp = pMsg->get_str( 1, 0, 0, 0 );
            int nLocalPort = pMsg->get_int( 1, 0 );
            LOG4( "init tcp server (%s:%d)", lpszLocalIp, nLocalPort );
            pConnect->set_local_addr( lpszLocalIp, nLocalPort );
            pConnect->set_net_connect( pNetConnect );
        }

        {
            helper_cat::get()->all_done_test( 1 );
        }

        // test code
        if( 0 == ret )
        {
            //test_tcp_connect( pMsg, pConnect );
        }

        pNetConnect->release_ref();

        return ret;
    }

    int notify_tcp::test_tcp_connect( xos_box::i_msg *& pMsg, connection * pServerConnect )
    {
        int ret = 0;
        
        connection::T * pConnect = 0;
        {
            xos_stl::init_pool_item( pConnect );
            pConnect->set_peer_addr( "127.0.0.1", 8080 );
            pConnect->m_pListenConnect = pServerConnect;
            port * pPort = pServerConnect->m_pPort;
            pConnect->m_pPort = pPort;
            pPort->m_pSslClientMgr->set_server_domain("www.cat.com");
            pConnect->m_pSsl = pPort->m_pSslClientMgr->ssl();
            pConnect->m_pSsl->init_ssl();
        }
        post_connect( pMsg, pConnect );
        pConnect = 0;
        
        return ret;
    }

    // bool[0]      : if success
    // 
    // msg[0]       : msg type
    // 
    // str[1]       : local ip
    // int[1]       : local port
    // 
    // str[2]       : peer ip
    // int[2]       : peer port
    // 
    // void[0]      : net::i_connection*
    // void[1]      : user data
    // 
    // 到这里，一定是成功的
    // 
    int notify_tcp::on_accept( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        net::i_connect * pNetConnect = (net::i_connect *)pMsg->get_void( 0, 0 );
        connection * pConnect = (connection *)pNetConnect->get_user_data();

        const char * lpszLocalIp = pMsg->get_str( 1, 0, 0, 0 );
        int nLocalPort = pMsg->get_int( 1, 0 );

        const char * lpszPeerIp = pMsg->get_str( 2, 0, 0, 0 );
        int nPeerPort = pMsg->get_int( 2, 0 );

        {
            LOG4( "tcp accept local = (%s:%d), peer = (%s:%d)", lpszLocalIp, nLocalPort, lpszPeerIp, nPeerPort );
        }

        {
            pConnect->set_net_connect( pNetConnect );
            pConnect->set_local_addr( lpszLocalIp, nLocalPort );
            pConnect->set_peer_addr( lpszPeerIp, nPeerPort );
        }

        pNetConnect->release_ref();

        return ret;
    }

    // bool[0]      : if success
    // 
    // msg[0]       : msg type
    // 
    // str[1]       : local ip
    // int[1]       : local port
    // 
    // str[2]       : peer ip
    // int[2]       : peer port
    // 
    // void[0]      : net::i_connection*
    // void[1]      : user data
    // 
    int notify_tcp::on_connect( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        net::i_connect * pNetConnect = (net::i_connect *)pMsg->get_void( 0, 0 );
        connection * pConnect = (connection *)pNetConnect->get_user_data();

        const char * lpszLocalIp = pMsg->get_str( 1, 0, 0, 0 );
        int nLocalPort = pMsg->get_int( 1, 0 );

        const char * lpszPeerIp = pMsg->get_str( 2, 0, 0, 0 );
        int nPeerPort = pMsg->get_int( 2, 0 );

        bool bRet = pMsg->get_bool( 0, 0 );

        // 稍后xos_net会回调on_release来释放这里的connection
        // 所以这里不要释放这个connection
        if( ( 0 == ret ) && !bRet )
        {
            pNetConnect->release_ref();
            pNetConnect = 0;
            ret = 1;
        }

        if( 0 == ret )
        {
            LOG4( "connect local = (%s:%d), peer = (%s:%d)", lpszLocalIp, nLocalPort, lpszPeerIp, nPeerPort );
        }

        if( 0 == ret )
        {
            pConnect->set_net_connect( pNetConnect );
            pConnect->set_local_addr( lpszLocalIp, nLocalPort );
            pConnect->set_peer_addr( lpszPeerIp, nPeerPort );
        }

        // test code
        if( 0 == ret )
        {
            //test_ssl_connect( pMsg, pConnect );
        }

        pNetConnect->release_ref();

        return ret;
    }

    int notify_tcp::test_ssl_connect( xos_box::i_msg *& pMsg, connection * pConnect )
    {
        int ret = 0;
        
        xos_box::i_list * pOut = pConnect->m_pSendNetEncodeDataList;
        xos_box::i_list * pIn = pConnect->m_pRecvDataList;
        xos_encrypt::i_ssl * pSsl = pConnect->m_pSsl;

        ret = pSsl->encode( pIn, pOut );
        if( 0 == ret )
        {
            ret = post_send( pConnect, pOut, false, false );
        }
        
        return ret;
    }

    // bool[0]      : if success
    // 
    // bool[1]      : close after send
    // bool[2]      : quit after send
    // 
    // msg[0]       : msg type
    // 
    // int[0]       : bytes
    // buf[0]       : buf
    // 
    // void[0]      : net::i_connection*
    // 
    int notify_tcp::on_send( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        net::i_connect * pNetConnect = (net::i_connect *)pMsg->get_void( 0, 0 );
        connection * pConnect = (connection *)pNetConnect->get_user_data();

        xos_box::i_buf * pBuf = pMsg->get_buf( 0, 0 );
        int nLen = pMsg->get_int( 0, 0 );

        bool bCloseAfterSend = pMsg->get_bool( 1, 0 );
        bool bQuitAfterSend = pMsg->get_bool( 2, 0 );

        bool bRet = pMsg->get_bool( 0, 0 );

        if( ( 0 == ret ) && !bRet )
        {
            ret = 1;
        }

        if( 0 == ret )
        {
            const char * lpszLocalIp = pConnect->get_local_ip( 0, 0, 0 );
            int nLocalPort = pConnect->get_local_port( 0 );
            const char * lpszPeerIp = pConnect->get_peer_ip( 0, 0, 0 );
            int nPeerPort = pConnect->get_peer_port( 0 );
            LOG4( "tcp send local = (%s:%d), peer = (%s:%d), len = %d", lpszLocalIp, nLocalPort, lpszPeerIp, nPeerPort, nLen );
        }

        if( ( 0 == ret ) && bCloseAfterSend )
        {
            LOG4( "tcp recv close after send" );
            post_close( pMsg, pConnect );
        }

        if( ( 0 == ret ) && bQuitAfterSend )
        {
            LOG4( "tcp recv quit after send" );
            helper_caller_callback::get()->notify( pMsg, net::NET_QUIT );
        }

        xos_stl::release_interface( pBuf );
        pNetConnect->release_ref();

        return ret;
    }

    // bool[0]      : if success
    // 
    // msg[0]       : msg type
    // 
    // int[0]       : bytes
    // buf[0]       : buf
    // 
    // void[0]      : net::i_connection*
    // 
    // recv一定是成功的
    // 
    int notify_tcp::on_recv( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        net::i_connect * pNetConnect = (net::i_connect *)pMsg->get_void( 0, 0 );
        connection * pConnect = (connection *)pNetConnect->get_user_data();

        xos_box::i_buf * pBuf = pMsg->get_buf( 0, 0 );
        int nLen = pMsg->get_int( 0, 0 );

        {
            const char * lpszLocalIp = pConnect->get_local_ip( 0, 0, 0 );
            int nLocalPort = pConnect->get_local_port( 0 );
            const char * lpszPeerIp = pConnect->get_peer_ip( 0, 0, 0 );
            int nPeerPort = pConnect->get_peer_port( 0 );
            LOG4( "tcp recv local = (%s:%d), peer = (%s:%d), len = %d", lpszLocalIp, nLocalPort, lpszPeerIp, nPeerPort, nLen );
        }

        // http proc
        //if( 0 )
        {
            pMsg->set_void( 0, pConnect );
            pNetConnect->add_ref();
            pConnect->fast_notify()->notify( pMsg, MSG_TYPE_FAST, FAST_PARSE_REQUEST );
            pBuf = 0;
        }

        // test code
        {
            //test_ssl_recv( pMsg, pConnect, pBuf );
        }

        xos_stl::release_interface( pBuf );
        pNetConnect->release_ref();

        return ret;
    }

    int notify_tcp::test_ssl_recv( xos_box::i_msg *& pMsg, connection * pConnect, xos_box::i_buf *& pBuf )
    {
        int ret = 0;
        
        xos_box::i_list * pSendBack = pConnect->m_pSendNetEncodeDataList;
        xos_box::i_list * pIn = pConnect->m_pRecvNetEncodeDataList;
        xos_box::i_list * pOut = pConnect->m_pRecvDataList;
        xos_encrypt::i_ssl * pSsl = pConnect->m_pSsl;

        if( 0 == ret )
        {
            pIn->push_back( pBuf );
            pBuf = 0;
            ret = pSsl->decode( pIn, pOut, pSendBack );
        }

        // decode返回值：
        // 
        //  0 : 解析数据成功
        //  1 : 连接成功
        //  2 : 连接断开
        // -1 : 连接失败，协议或数据错误。服务端接受连接时才会发生，此时会保留全部数据，以便可以利用它来切换到非ssl的明文协议状态
        // -2 : 连接需要更多数据
        // -3 : 解析失败，协议或数据错误
        // -4 : 解析需要更多数据
        // -5 : 写入BIO失败，不太可能

        switch( ret )
        {
            //  0 : 解析数据成功
            case 0:
            {
                char buf[1024] = {0};
                pOut->get_data( buf, 1024 );
                LOG4("recv data = %s", buf);
                pOut->release_all();
            }
            break;
            //  1 : 连接成功
            case 1:
            {
                pIn->add_str( "test", true );
                pSsl->encode( pIn, pOut );
            }
            break;
            //  2 : 连接断开
            case 2:
            {
            }
            break;
            // -1 : 连接失败，协议或数据错误。服务端接受连接时才会发生，此时会保留全部数据，以便可以利用它来切换到非ssl的明文协议状态
            case -1:
            {
            }
            break;
            // -2 : 连接需要更多数据
            case -2:
            {
            }
            break;
            // -3 : 解析失败，协议或数据错误
            case -3:
            {
            }
            break;
            // -4 : 解析需要更多数据
            case -4:
            {
            }
            break;
            // -5 : 写入BIO失败，不太可能
            case -5:
            {
            }
            break;
        }

        if( pSendBack->size() > 0 )
        {
            post_send( pConnect, pSendBack, false, false );
        }

        if( pOut->size() > 0 )
        {
            post_send( pConnect, pOut, false, false );
        }
        
        return ret;
    }

    // bool[0]      : if success
    // 
    // msg[0]       : msg type
    // 
    // void[0]      : net::i_connection*
    // 
    int notify_tcp::on_shutdown( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        net::i_connect * pNetConnect = (net::i_connect *)pMsg->get_void( 0, 0 );
        connection * pConnect = (connection *)pNetConnect->get_user_data();

        if( 0 == ret )
        {
            const char * lpszLocalIp = pConnect->get_local_ip( 0, 0, 0 );
            int nLocalPort = pConnect->get_local_port( 0 );
            const char * lpszPeerIp = pConnect->get_peer_ip( 0, 0, 0 );
            int nPeerPort = pConnect->get_peer_port( 0 );
            LOG4( "tcp shutdown local = (%s:%d), peer = (%s:%d)", lpszLocalIp, nLocalPort, lpszPeerIp, nPeerPort );
        }

        pNetConnect->release_ref();

        return ret;
    }

    // bool[0]      : if success
    // 
    // msg[0]       : msg type
    // 
    // void[0]      : net::i_connection*
    // 
    int notify_tcp::on_close( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        net::i_connect * pNetConnect = (net::i_connect *)pMsg->get_void( 0, 0 );
        connection * pConnect = (connection *)pNetConnect->get_user_data();

        if( 0 == ret )
        {
            const char * lpszLocalIp = pConnect->get_local_ip( 0, 0, 0 );
            int nLocalPort = pConnect->get_local_port( 0 );
            const char * lpszPeerIp = pConnect->get_peer_ip( 0, 0, 0 );
            int nPeerPort = pConnect->get_peer_port( 0 );
            LOG4( "tcp close local = (%s:%d), peer = (%s:%d)", lpszLocalIp, nLocalPort, lpszPeerIp, nPeerPort );
        }

        pNetConnect->release_ref();

        return ret;
    }

} // xos_http
